#include "things.h"
#include <esp_log.h>

#define TAG "things"

thing_t *thing_create(char *name, char *description, method_list_t *methods, property_list_t *properties)
{
    thing_t *thing = (thing_t *)object_new(sizeof(thing_t));
    assert(thing);

    thing->name = strdup(name);
    thing->description = strdup(description);
    thing->methods = methods;
    thing->properties = properties;
    return thing;
}

cJSON *thing_get_descriptor_json(thing_t *thing)
{
    cJSON *root = cJSON_CreateObject();
    assert(root);
    cJSON_AddStringToObject(root, "name", thing->name);
    cJSON_AddStringToObject(root, "description", thing->description);
    cJSON_AddItemToObject(root, "methods", method_list_get_descriptor_json(thing->methods));
    cJSON_AddItemToObject(root, "properties", property_list_get_descriptor_json(thing->properties));
    return root;
}

cJSON *thing_get_state_json(thing_t *thing)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddStringToObject(root, "name", thing->name);
    cJSON_AddItemToObject(root, "state", property_list_get_state_json(thing->properties));
    return root;
}

cJSON *thing_list_get_descriptor_json(thing_list_t *list)
{
    cJSON *root = cJSON_CreateArray();
    assert(root);
    foreach_in_list(thing_t *, thing, list)
    {
        cJSON_AddItemToArray(root, thing_get_descriptor_json(thing));
    }
    return root;
}

cJSON *thing_list_get_state_json(thing_list_t *list)
{
    cJSON *root = cJSON_CreateArray();
    assert(root);
    foreach_in_list(thing_t *, thing, list)
    {
        cJSON_AddItemToArray(root, thing_get_state_json(thing));
    }
    return root;
}

thing_t *thing_list_get(thing_list_t *list, char *name)
{
    foreach_in_list(thing_t *, thing, list)
    {
        // 挨个比较名字，匹配返回
        if (strcmp(thing->name, name) == 0)
        {
            return thing;
        }
    }
    // 找不到匹配项返回NULL
    return NULL;
}

void thing_invoke(thing_t *thing, cJSON *command)
{
    cJSON *method_name = cJSON_GetObjectItem(command, "method");
    if (cJSON_IsString(method_name))
    {
        // 根据method名字，获取method
        method_t *method = method_list_get(thing->methods, method_name->valuestring);

        // 解析参数的值，传入method_parameters，并调用method回调
        cJSON *method_parameters = cJSON_GetObjectItem(command, "parameters");
        if (!method_parameters)
        {
            ESP_LOGW(TAG, "method_parameters is null");
            return;
        }

        // 遍历method的参数列表，匹配每一个值
        foreach_in_list(property_t *, param, method->parameters)
        {
            // 根据函数的参数名称，获取实际的参数值
            cJSON *param_json = cJSON_GetObjectItem(method_parameters, param->name);
            if (!param_json)
            {
                ESP_LOGW(TAG, "Requried parameter is null");
                return;
            }

            // 根据参数类型，解析传入参数值
            switch (param->type)
            {
            case PROPERTY_VALUE_TYPE_BOOL:
                param->value.boolean = cJSON_IsTrue(param_json);
                break;
            case PROPERTY_VALUE_TYPE_NUMBER:
                param->value.number = param_json->valueint;
                break;
            case PROPERTY_VALUE_TYPE_STRING:
                param->value.str = strdup(param_json->valuestring);
                break;
            default:
                break;
            }
        }
        method->callback(method->context, method->parameters);

        // 释放strdup 分配的内存
        foreach_in_list(property_t *, param, method->parameters)
        {
            if (param->type == PROPERTY_VALUE_TYPE_STRING && param->value.str)
            {
                free(param->value.str);
                param->value.str = NULL;
            }
        }
    }
}

void thing_list_invoke(thing_list_t *list, cJSON *command_array)
{
    for (size_t i = 0; i < cJSON_GetArraySize(command_array); i++)
    {
        cJSON *command = cJSON_GetArrayItem(command_array, i);

        // 找到要设置的thing是哪个
        cJSON *thing_name = cJSON_GetObjectItemCaseSensitive(command, "name");
        if (cJSON_IsString(thing_name))
        {
            // 按照名字找到thing
            thing_t *thing = thing_list_get(list, thing_name->valuestring);

            // 找到后执行命，找不到打印错误日志
            if (thing)
            {
                thing_invoke(thing, command);
            }
            else
            {
                ESP_LOGW(TAG, "thing %s not found", thing_name->valuestring);
            }
        }
    }
}
